import asyncio
from asyncio import Future
from decimal import Decimal
from typing import *

import pandas as pd

from utils.error import TradeError, BaseError
from utils.trade.tradeType import BaseTradeType
from utils.strategy.conf.base import BaseConf
from loguru import logger
from utils.exchange.binance import BinanceBase


class TradeMeta(type):
    """
    交易逻辑元类
    """
    # 所有的子类类型
    ALL: Dict[str, type] = {}

    def __init__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
        super().__init__(name, bases, attrs)
        if not hasattr(cls, 'trade') and not callable(getattr(cls, 'trade', None)):
            raise TradeError(f'必须在交易类型中实现\'trade\'方法')

    def __new__(cls, name: str, bases: Tuple[type, ...], attrs: Dict[str, Any]):
        sub_class = type.__new__(cls, name, bases, attrs)
        cls.ALL[name] = sub_class
        return sub_class

    def __getitem__(self, item):
        return self.ALL[item]


class BaseTradeStrategy(metaclass=TradeMeta):
    """
    交易基类
    继承此类型的类在实例化时直接执行trade方法，必须实现静态方法trade，类会被存储在元类的ALL属性中
    """
    desc: str = "交易基类"

    def __init__(self, api_key: str, secret_key: str) -> None:
        self.api_key = api_key
        self.secret_key = secret_key
        self.spot_balance: Dict[str, Decimal] = {}
        self.swap_balance: Dict[str, Dict[str, Decimal]] = {}
        self.dataframe: pd.DataFrame = pd.DataFrame()
        self.prepare_data()


    async def prepare_data(self):
        """"""
        binance = BinanceBase(self.api_key, self.secret_key, async_=True)
        tasks: List[Future] = [binance.kline(), binance.spot_balance(), binance.swap_balance()]
        resp = await asyncio.gather(*tasks)


    def trade(self, conf: BaseConf, trader: BaseTradeType, *args, **kwargs):
        raise TradeError(f'交易基类不会进行任何操作')


class CTA(BaseTradeStrategy):

    desc = """
    适用于单向持仓、单币种、单信号、逐仓、纯合约，交易模式为吃单交易
    """


    def trade(self, conf: BaseConf, trader: BaseTradeType, *args, **kwargs):
        df: pd.DataFrame = args[0]

        sig = df['signal'][-1]
        print(sig)
        if sig not in [0, 1, -1]:
            return

        symbol: str = conf.strategy.symbols[0]
        trader.transfer('USDT', trader.exchange.spot_balance().get('USDT', Decimal(0)), 'MAIN_UMFUTURE')
        amount: Decimal = trader.exchange.swap_balance().get('USDT', {'maxWithdrawAmount': Decimal(0)})[
            'maxWithdrawAmount']
        quantity: Decimal = amount * Decimal('0.95') / trader.exchange.swap_symbol_prices[symbol]
        pos_swap = pd.DataFrame(
            [[k['symbol'], k['positionAmt'], 1, k['leverage']] for k in trader.exchange.swap_position() if
             k['symbol'] == symbol],
            columns=['symbol', 'position', 'trade_type', 'leverage'])

        if pos_swap.empty:
            position = 0
        else:
            position = pos_swap.loc[pos_swap['symbol'] == symbol, 'position'].values[0]

        # 信号和当前持仓方向相同则不进行操作
        if position * sig > 0:
            return

        if sig != 0:
            target = quantity * Decimal(sig) + (2 * position if position * Decimal(sig) < 0 else 0)
            trader.swap_trade([{'symbol': symbol, 'positionSide': 'BOTH', 'quantity': abs(target),
                                'type': 'MARKET', 'side': 'buy' if sig > 0 else 'sell',
                                'reduceOnly': False}])
        else:
            trader.swap_trade([{'symbol': symbol, 'positionSide': 'BOTH', 'quantity': abs(position) * 2,
                                'type': 'MARKET', 'side': 'buy' if position < 0 else 'sell',
                                'reduceOnly': True}])


